package flib;

import java.awt.geom.Line2D;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Collections;
import java.util.Vector;

import processing.core.PApplet;
import processing.core.PGraphics;
import simpleGeom.ConvexHull;

import simpleGeom.HasPos2D;
import simpleGeom.Polygon2D;
import simpleGeom.Rect2D;
import simpleGeom.Vec2D;


public class Situations2D extends PApplet {
	Vector<Index> indexes;
	Vector<Plan> plans;

	float fShape = 2;
	float fArea = 0;
	float fProp = 0;
	float fDim = 0.1f;
	float[] factors = new float[] { 1, 0, 1, 1 };
	String pathFolder = "/Users/administrator/Desktop/subSituations/";

	Vector<Polygon2D> polys;

	boolean doSort;

	int cIndex = 0;

	public void setup() {
		size(1400, 900);
		loadAll(pathFolder);
		createPolys();
	}

	public void sort(Index index) {
		IndexComparator ic = new IndexComparator();
		for (int i = 0; i < indexes.size(); i++) {
			Index cIndex = indexes.get(i);
			calculateDist(cIndex, index);
		}
		float[] minDs = new float[factors.length];
		float[] maxDs = new float[factors.length];
		for (int i = 0; i < factors.length; i++) {
			minDs[i] = Float.MAX_VALUE;
			maxDs[i] = Float.NEGATIVE_INFINITY;
		}

		// normalise;
		for (int i = 0; i < indexes.size(); i++) {
			Index cIndex = indexes.get(i);
			for (int j = 0; j < cIndex.distValues.length; j++) {
				float cD = cIndex.distValues[j];
				if (minDs[j] > cD)
					minDs[j] = cD;
				if (maxDs[j] < cD)
					maxDs[j] = cD;
			}
		}
		for (int j = 0; j < minDs.length; j++) {
			float minD = minDs[j];
			float maxD = maxDs[j];
			float deltaD = maxD - minD;
			for (int i = 0; i < indexes.size(); i++) {
				Index cIndex = indexes.get(i);
				float cD = cIndex.distValues[j];
				cIndex.distValues[j] = (cD - minD) / deltaD;
			}
		}
		// sumdDistance;
		for (int i = 0; i < indexes.size(); i++) {
			Index cIndex = indexes.get(i);
			float cD = 0;
			for (int j = 0; j < cIndex.distValues.length; j++) {
				cD += factors[j] * cIndex.distValues[j];
			}
			cIndex.distance = cD;
		}
		Collections.sort(indexes, ic);
		println(indexes.firstElement().distance);
	}

	public void createPolys() {
		polys = new Vector<Polygon2D>();
		Polygon2D poly = new Polygon2D();
		/*poly.addPt(new Vec2D(0, 0));
		poly.addPt(new Vec2D(7, 0));
		poly.addPt(new Vec2D(9, 10));
		poly.addPt(new Vec2D(5, 13));
		poly.addPt(new Vec2D(0, 10));
		poly.addPt(new Vec2D(-2, 5));*/
		
		poly.addPt(new Vec2D(0, 0));
		poly.addPt(new Vec2D(8, 0));
		poly.addPt(new Vec2D(10, 12));
		poly.addPt(new Vec2D(6, 15));
		poly.addPt(new Vec2D(0, 12));
		poly.addPt(new Vec2D(-4, 7));
		
		
		/* poly = new Polygon();
		poly.addPt(new Vec2D(0, 0));
		poly.addPt(new Vec2D(9, 0));
		poly.addPt(new Vec2D(9, 10));
		poly.addPt(new Vec2D(-5, 10));
		poly.addPt(new Vec2D(-5, 5));
		poly.addPt(new Vec2D(0, 5));*/
		polys.add(poly);
	}

	public void fitPlan(Polygon2D poly, Plan plan) {
		Vector<FlibHEdge> hedges = plan.getAllHEdges();
		
		Rect2D b=getBounds(plan);
		// kopieren
		Vector<FlibHENode> newNodes = new Vector<FlibHENode>();
		Vector<FlibHEdge> newHEdges = new Vector<FlibHEdge>();
		float rot=random(-0.2f,0.2f);
		for (int i = 0; i < plan.nodes.size(); i++) {
			FlibHENode n = plan.nodes.get(i);
			n.id = i;
			FlibHENode nNew = new FlibHENode(n.x-b.getCenterX(), n.y-b.getCenterY(), n.z);
			//Vec2D pos=Vec2D.rotate(rot, 0, 0, nNew.getX(), nNew.getY());
			//nNew.setPosition(pos.x, pos.y);
			newNodes.add(nNew);
		}
		Vector<FlibWall>newWalls=new Vector<FlibWall>();
		for (int i = 0; i < plan.walls.size(); i++) {
			FlibWall wall = plan.walls.get(i);
			FlibHEdge hEdge=wall.hEdge;
			
			
			if (hEdge.getFace()!=null||hEdge.getSym().getFace()!=null){
			FlibHENode n1 = newNodes.get(hEdge.getOrg().getId());
			FlibHENode n2 = newNodes.get(hEdge.getDest().getId());
			
			FlibWall newWall=new FlibWall(n1,n2);
			newWall.type=wall.type;
			stroke(0,0,255);
			//newWall.display2D(g);
			newHEdges.add(newWall.hEdge);
			newHEdges.add(newWall.hEdge.getSym());
			newWall.hEdge.attach();
			newWalls.add(newWall);
			}
		}
		Vector<Room> rooms = new Vector<Room>();
		for (int i = 0; i < newHEdges.size(); i++) {
			FlibHEdge hEdge = newHEdges.get(i);
			if (hEdge.getFace() == null) {
				Room room = new Room();
				room.setHEdge(hEdge);
				hEdge.setFaceToAll(room);
				if (room.getArea2D() < 0) {
					hEdge.setFaceToAll(null);
				} else {
					rooms.add(room);
				}
			}
		}
		Vector<FlibWall>newWalls2=new Vector<FlibWall>();
		for (int i = 0; i < newWalls.size(); i++) {
			FlibWall wall = newWalls.get(i);
			stroke(0,255,255);
			FlibHEdge hEdge=wall.hEdge;
			//line(hEdge.x1(),hEdge.getY1(),hEdge.x2(),hEdge.getY2());
			newWalls2.add(wall);
			/*if (wall.hEdge.getFace()!=null&&wall.hEdge.getSym().getFace()!=null){
				newWalls2.add(wall);
			}
			else{
				wall.hEdge.detach();
			}*/
		}
		stroke(255,0,0);
		for (int i=0;i<newWalls2.size();i++){
			FlibWall wall=newWalls2.get(i);
			//all.display2D(g);
		}
		Vector<FlibHENode>newNodes2=new Vector<FlibHENode>();
		for (int i=0;i<newNodes.size();i++){
			FlibHENode n=newNodes.get(i);
			if (n.getHEdge()!=null){
				newNodes2.add(n);
			}
		}
		for (int i=0;i<newNodes2.size();i++){
			FlibHENode n=newNodes2.get(i);
			n.setId(1);
		}
		stroke(0,0,255);
		Vector<FlibHENode>allNodes=new Vector<FlibHENode>();
		for (int i=0;i<newWalls2.size();i++){
			FlibWall wall=newWalls2.get(i);
			FlibHEdge hEdge=wall.hEdge;
			Room room=(Room) hEdge.getFace();
			if (room==null){
				FlibHEdge cHE=hEdge;
				do{
					//line(cHE.x1(),cHE.getY1(),cHE.x2(),cHE.getY2());
					allNodes.add(cHE.getOrg());
					cHE=cHE.getNext();
					
				}while(cHE!=hEdge);
				break;
			}
			hEdge=hEdge.getSym();
			room=(Room) hEdge.getFace();
			if (room==null){
				FlibHEdge cHE=hEdge;
				do{
					allNodes.add(cHE.getOrg());
					//line(cHE.x1(),cHE.getY1(),cHE.x2(),cHE.getY2());
					cHE=cHE.getNext();
				}while(cHE!=hEdge);
				break;
			}
		}
		
		ConvexHull cHull=new ConvexHull(allNodes);
		for (int i=0;i<cHull.hull.size();i++){
			HasPos2D cP=cHull.hull.get(i);
			cP.setId(-1);
		}
		for (int i=0;i<allNodes.size();i++){
			FlibHENode n= allNodes.get(i);
			if (n.getId()>0){
				for (int j=0;j<cHull.hull.size();j++){
					HasPos2D cP1=cHull.hull.get(j);
					HasPos2D cP2=cHull.hull.get((j+1)%cHull.hull.size());
					double cD=Line2D.ptSegDist(cP1.getX(), cP1.getY(), cP2.getX(), cP2.getY(), n.getX(), n.getY());
					if (cD<0.01f){
						n.setId(-1);
					}
				}
			}
		}
		fill(0,255,0);
		
			Vector<FlibWall>newWalls3=new Vector<FlibWall>();
		for (int i=0;i<newWalls2.size();i++){
			FlibWall wall=newWalls2.get(i);
			if (wall.getN1().getId()<0&&wall.getN2().getId()<0){
				wall.hEdge.detach();
			}
			else{
				stroke(255,0,255);
				newWalls3.add(wall);
				//wall.display2D(g);
			}
		}
		for (int i=0;i<newWalls3.size();i++){
			FlibWall wall=newWalls3.get(i);
			if (wall.getN1().getNHEdges()==1&&wall.getN1().getId()<0){
				stroke(0,255,255);
				Vec2D v=wall.hEdge.getSym().getDir();
				v.setLength(5);
				wall.hEdge.getOrg().add(v);
				//wall.display2D(g);
			}
			else if (wall.getN2().getNHEdges()==1&&wall.getN2().getId()<0){
				stroke(0,255,255);
				Vec2D v=wall.hEdge.getDir();
				v.setLength(5);
				wall.hEdge.getDest().add(v);
				//wall.display2D(g);
			}
		}
		b = poly.getBounds();
		Vector<FlibHENode>borderNodes=new Vector<FlibHENode>();
		for (int i=0;i<poly.getNPts();i++){
			HasPos2D cP=poly.get(i);
			FlibHENode node=new FlibHENode(cP.getX()-b.getCenterX(),cP.getY()-b.getCenterY());
			borderNodes.add(node);
			newNodes2.add(node);
			
		}
		Polygon2D polyShifted=new Polygon2D();
		polyShifted.setPts(borderNodes);
		for (int i=0;i<borderNodes.size();i++){
			FlibHENode node1=borderNodes.get(i);
			FlibHENode node2=borderNodes.get((i+1)%borderNodes.size());
			FlibWall wall=new FlibWall(node1,node2);
			wall.hEdge.attach();
			newWalls3.add(wall);
			
		}
		FlibGraph group=new FlibGraph();
		group.nodes=newNodes2;
		group.walls=newWalls3;
		group.intersect();
		
		
		for (int i=0;i<group.walls.size();i++){
			FlibWall wall=group.walls.get(i);
			wall.hEdge.setFace(null);
			wall.hEdge.getSym().setFace(null);
			stroke(0);
			//wall.display2D(g);
			//rect(wall.x1(),wall.y1(),0.1f,0.1f);
		}
		fill(0,255,0);
		
		for (int i=0;i<group.walls.size();i++){
			FlibWall wall=group.walls.get(i);
			wall.hEdge.detach();
		}
		for (int i=0;i<group.nodes.size();i++){
			FlibHENode node=group.nodes.get(i);
			node.emptyHEdges();
		}
		group.attach();
		group.createRooms();
		polyShifted.calculateBounds();
		for (int i=0;i<group.rooms.size();i++){
			Room room=group.rooms.get(i);
			Vec2D c=room.getGravityCenter();
			if (!polyShifted.contains(c.x, c.y)){
				room.detach();
				group.rooms.remove(room);
				i--;
			}
			else{
			stroke(0);
			fill(255);
			//fill(i*255f/group.rooms.size(),255-i*255f/group.rooms.size(),255);
			room.display(g);
			}
		}
		rectMode(CENTER);
		for (int i=0;i<group.walls.size();i++){
			FlibWall wall=group.walls.get(i);
			FlibHEdge hEdge=wall.hEdge;
			if (hEdge.getFace()!=null||hEdge.getSym().getFace()!=null){
			stroke(255,0,0);
			if (wall.type!=null&&wall.type.permeable){
				stroke(0,255,0);
			}
			wall.display2D(g);
			}
			//rect(wall.x1(),wall.y1(),0.2f,0.2f);
		}
	}

	public void draw() {
		if (doSort) {
			println("doSort");
			Index index = createIndex(polys);
			sort(index);
			doSort = false;
			println("sorted");
		}
		background(0);
		Rect2D bounds = getBounds(polys);
		float scaleF = 20;
		scale(scaleF);
		translate(width * 0.5f / scaleF, height * 0.5f / scaleF);
		strokeWeight(0.1f);

		noFill();
		stroke(255, 0, 0);
		pushMatrix();
		translate(-bounds.getCenterX(), -bounds.getCenterY());

		for (int i = 0; i < polys.size(); i++) {
			Polygon2D poly = polys.get(i);
			poly.display(g);
		}

		popMatrix();
		noFill();
		stroke(0);
		pushMatrix();
		bounds = getBounds(polys);

		Index index = indexes.get(cIndex);

		Plan plan = (Plan) index.indexed;
		Rect2D b = getBounds(plan);
		
		pushMatrix();
		fill(0, 255, 0);
		translate(-b.getCenterX(), -b.getCenterY());
		for (int i = 0; i < plan.rooms.size(); i++) {
			Room room = plan.rooms.get(i);
			//room.display(g);
		}
		noFill();
		popMatrix();
		fitPlan(polys.firstElement(),plan);
		
		// translate(i.w,i.h);

		// i=indexes.lastElement();
		// translate(i.w,i.h);
		// rect(0,0,i.w,i.h);
		popMatrix();
	}
	public Rect2D getBounds(Plan plan){
		Rect2D b = null;
		for (int i = 0; i < plan.rooms.size(); i++) {
			if (i == 0) {
				b = plan.rooms.get(i).getBounds2D();
			} else {
				b.addRect2D(plan.rooms.get(i).getBounds2D());
			}
		}
		return b;
	}
	public Rect2D getBounds(Vector<Polygon2D> polys) {
		Rect2D r = new Rect2D();
		r = polys.firstElement().getBounds2D();
		for (int i = 1; i < polys.size(); i++) {
			r.addRect2D(polys.get(i).getBounds2D());
		}
		return r;
	}

	public Index createIndex(Vector<Polygon2D> polys) {
		Rect2D r = getBounds(polys);

		PGraphics indexGraphics = (PGraphics) this.createGraphics(16, 16,
				P2D);
		indexGraphics.beginDraw();
		indexGraphics.background(255);

		float scaleX = 16f / r.getDimX();
		float scaleY = 16f / r.getDimY();
		indexGraphics.pushMatrix();
		indexGraphics.scale(scaleX, scaleY);
		indexGraphics.translate(-r.x1, -r.y1);
		indexGraphics.noStroke();
		indexGraphics.fill(0);
		for (int i = 0; i < polys.size(); i++) {
			Polygon2D poly = polys.get(i);
			poly.display(indexGraphics);
		}
		indexGraphics.popMatrix();
		boolean[][] index = new boolean[16][16];
		for (int x = 0; x < indexGraphics.width; x++) {
			for (int y = 0; y < indexGraphics.height; y++) {
				int col = indexGraphics.get(x, y);
				if (brightness(col) < 100) {
					index[x][y] = true;
				}
			}
		}
		Index cIndex = new Index(index, r.getDimX(), r.getDimY());
		return cIndex;
	}

	public void loadAll(String pathFolder) {
		indexes = new Vector<Index>();
		plans = new Vector<Plan>();
		FloorplanLoader2D loader=new FloorplanLoader2D();
		Vector<File> files = getFilesFromFolder(pathFolder, ".txt");
		for (int i = 0; i < files.size(); i++) {
			File file = files.get(i);
			Index index = new Index(this, file.getAbsolutePath());
			indexes.add(index);
			String objPath = file.getAbsolutePath();
			objPath = objPath.replace(".txt", ".obj");
			Plan plan = loader.loadFromOBJ(this, objPath);
			plans.add(plan);
			index.indexed = plan;
		}
	}

	public void calculateDist(Index i1, Index i2) {
		float dShape = 0;
		for (int x = 0; x < 16; x++) {
			boolean[] col1 = i1.index[x];
			boolean[] col2 = i2.index[x];
			for (int y = 0; y < 16; y++) {
				boolean v1 = col1[y];
				boolean v2 = col2[y];
				if (v1 != v2)
					dShape++;
			}
		}
		dShape /= 256f;

		float dArea = Math.abs(i1.area - i2.area);
		float dProp = Math.abs(i1.proportion - i2.proportion);
		float dW = Math.abs(i1.w - i2.w);
		float dH = Math.abs(i1.h - i2.h);
		float dDim = (float) Math.sqrt(dW * dW + dH * dH);
		float[] distances = new float[4];
		distances[0] = dShape;
		distances[1] = dArea;
		distances[2] = dProp;
		distances[3] = dDim;
		i1.distValues = distances;

	}

	public Vector<File> getFilesFromFolder(String folderPath, String ending) {
		File dir = new File(folderPath);
		Vector<File> files = new Vector<File>();
		File[] dirfiles = dir.listFiles();
		if (dirfiles == null) {
			// Either dir does not exist or is not a directory
		} else {
			for (int i = 0; i < dirfiles.length; i++) {
				// Get filename of file or directory
				File file = dirfiles[i];
				if (file.getName().endsWith(ending)) {
					files.add(file);
				}
			}
		}
		return files;
	}

	public void keyPressed() {
		if (key == 'f') {
			doSort = true;
		}
		if (key == 'i') {
			cIndex++;
		}
	}

}
